package com.lvcoding.miaoshademo.service;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.lvcoding.miaoshademo.domain.StockOrder;
import com.lvcoding.miaoshademo.domain.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lvcoding.miaoshademo.mapper.StockMapper;
import com.lvcoding.miaoshademo.domain.Stock;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.Date;
import java.util.concurrent.TimeUnit;


/**
 * 使用乐观锁解决商品超卖问题，实际上是把主要防止超卖问题交给数据库解决，
 * 利用数据库中定义的version字段以及数据库中的事物实现
 * 在并发情况下商品的超卖问题
 *
 * @author wuyanshen
 */
@Slf4j
@Service
@Transactional
public class StockServiceImpl extends ServiceImpl<StockMapper, Stock> implements StockService {

    @Autowired
    private StockOrderService stockOrderService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private UserService userService;


    @Override
    public Integer kill(Integer id) {

        // Redis实现限时抢购
        // 校验redis中秒杀商品是否超时
        boolean haskey = this.stringRedisTemplate.hasKey("kill" + id);
        if(!haskey) {
            log.info("秒杀活动已经结束啦");
            throw new RuntimeException("秒杀活动已经结束啦");
            // return null;
        }

        // 校验库存
        Stock stock = checkStock(id);

        // 扣减库存
        boolean flag = updateSale(stock);

        if(flag) {
            // 创建订单
            return createOrder(stock);
        }

        return null;
    }

    @Override
    public String getMd5(Integer userId, String goodId) {
        User user = this.userService.getById(userId);
        if (user == null) {
            throw new RuntimeException("用户不合法");
        }
        log.info("用户信息:[{}]", user.toString());

        Stock stock = this.getById(goodId);
        if (stock == null) {
            throw new RuntimeException("商品不合法");
        }
        log.info("商品信息:[{}]", stock.toString());

        // 生成hashkey
        String hashKey = "KEY_" + userId + "_" + goodId;
        // 6位随机字符串
        String randomStr6 = RandomUtil.randomString(6);
        // 生成md5
        String md5 = DigestUtils.md5DigestAsHex((userId + goodId + randomStr6).getBytes());
        // 存入redis中
        this.stringRedisTemplate.opsForValue().set(hashKey, md5, 60, TimeUnit.SECONDS);
        log.info("redis写入:[{}] [{}]", userId, goodId);

        return md5;
    }

    @Override
    public Integer kill(Integer id, Integer userId, String md5) {

        if (id == null) {
            throw new RuntimeException("id为空，当前请求不合法");
        }

        if (userId == null) {
            throw new RuntimeException("userId为空，当前请求不合法");
        }

        if (md5 == null) {
            throw new RuntimeException("md5为空，当前请求不合法");
        }

        // TODO 验证商品是否到了秒杀时间

        // Redis实现限时抢购
        // 校验redis中秒杀商品是否超时
        boolean haskey = this.stringRedisTemplate.hasKey("kill" + id);
        if(!haskey) {
            log.info("秒杀活动已经结束啦");
            throw new RuntimeException("秒杀活动已经结束啦");

            // return null;
        }

        // 验证签名
        String hashKey = "KEY_" + userId + "_" + id;
        String value = this.stringRedisTemplate.opsForValue().get(hashKey);
        if(StrUtil.isNotBlank(value)&&!value.equals(md5) || StrUtil.isBlank(value)) {
            throw new RuntimeException("验签失败，当前请求不合法");
        }

        // 校验库存
        Stock stock = checkStock(id);

        // 扣减库存
        boolean flag = updateSale(stock);

        if(flag) {
            // 创建订单
            return createOrder(stock);
        }

        return null;
    }

    @Override
    public void startMiaosha(Integer goodId, Long time) {

        if(goodId == null) {
            throw new RuntimeException("活动的商品id不能为空");
        }

        // 默认的秒杀活动时间，单位(秒)
        long expTime = 120;

        if (time != null) {
            expTime = time;
        }

        // 活动的key
        String huodongKey = "kill" + goodId;

        this.stringRedisTemplate.opsForValue().set(huodongKey, "1", expTime, TimeUnit.SECONDS);
    }

    // 校验库存
    private Stock checkStock(Integer id) {
        Stock stock = this.getById(id);
        if(stock.getSale().equals(stock.getCount())) {
            log.info("库存不足！！！");
            // throw new RuntimeException("库存不足");
            return null;
        }

        return stock;
    }

    // 扣减少库存
    private boolean updateSale(Stock stock) {
        // stock.setSale(stock.getSale() + 1);
        // this.updateById(stock);

        // 乐观锁解决超卖
        // int num = this.baseMapper.updateSale(stock);
        // if(num == 0) {
        //     throw new RuntimeException("抢购失败，请重试");
        // }
        if (stock != null) {
            stock.setSale(stock.getSale() + 1);
            boolean flag = this.updateById(stock);
            if(!flag) {
                log.info("抢购失败，请重试");

                // 这里删掉后会出现超卖
                // throw new RuntimeException("抢购失败，请重试");
                return false;
            }
        }
        return true;
    }

    // 创建订单
    private Integer createOrder(Stock stock) {
        if (stock != null) {
            StockOrder stockOrder = new StockOrder();
            stockOrder.setSid(stock.getId()).setName(stock.getName()).setCreateTime(new Date());
            stockOrderService.save(stockOrder);
            return stockOrder.getId();
        }

        return null;
    }
}

